/** @file projection.h
    @brief OpenGL projections */

#ifndef G3_PROJECTION_H
#define G3_PROJECTION_H

#include "GLee/GLee.h"

#include "GLee/GLee.h"

#include <GL/gl.h>
#include <GL/glu.h>

#include "types.h"
#include "math/m_bounding.h"
#include "render/r_frustum.h"

namespace G3 {

/** @class RENDER_TO_TEXTURE
    @brief A class that renders to texture */
/** @class RENDER_TO_TEXTURE
    @brief A class that renders to texture */
class RENDER_TO_TEXTURE {
	public:
        /**
            @brief Constructor
        */
		RENDER_TO_TEXTURE ();
        /**
            @brief Destructor
        */
		~RENDER_TO_TEXTURE ();

        /** Texture viewport coordinates */
		G3::RECTI TextureDimensions;
		/** The previous viewport coordinates (restored after the End function call) */
		G3::RECTI ViewportDimensions;
		/** Texture format */
		GLenum Format;
		/** Which level of detail is overwritten ? */
		int LevelOfDetail;
		/** The target texture ID */
		uint TextureTargetID;
		/** FBO ID */
		uint fboID;
		/** FBO Depth Buffer */
		uint fboDepthID;

		bool useFBO;

        /** Pointer to the log */
		G3::LOG *log;

        /**
            @brief Initializes the class.
            If FrameBufferObject is supported, then initializes it.
            @note Should be called after glSelectDimensions and SelectTexture
        */
		void Init ();

        /**
            @brief Selects target texture dimensions according to the current preferences
            The preferences specified in the TextureDimensions member.
        */
		void glSelectDimensions ();

        /**
            @brief Selects target texture dimensions according to the current preferences
            @param[in] TextureDim Texture dimensions
        */
		void glSelectDimensions (G3::RECTI TextureDim);

        /**
            @brief Selects a target texture
            @param[in] TextureID The texture index
        */
		void SelectTexture (uint TextureID = 0);

        /**
            @brief Begins the rendering to the texture
            @param[in] TextureID Target texture id in case it has changed or UINT_MAX if it hasn't
            @param[in] aFormat Target texture format
            @param[in] aLevelOfDetail Which level of detail is overwritten ?
            @note Uses FrameBufferObjects if supported or traditional glCopyTexImage2D if not
        */
		void Begin (uint TextureID = UINT_MAX, GLenum aFormat = GL_RGBA, int aLevelOfDetail = 0);

		/**
            @brief Ends the rendering to the texture process
		*/
		void End ();
};

/**
    @brief A replacement of gluPerspective
    @param[in] View Reference to the view coordinates
    @param[in] Near Near plane distance
    @param[in] Far Far plane distance
    @param[in] FieldOfView FOV angle
*/
void glSetPerspective (const RECTI &View, float Near, float Far, float FieldOfView);

/**
    @brief A replacement of gluPerspective
    @param[in] View Reference to the view coordinates
    @param[out] Frustum Pointer to the view frustum
    @note This one also modifies the view frustum
*/
void glSetPerspective (const RECTI &View, FRUSTUM *Frustum);

/**
    @brief Subfunction for the glSetPerspective (1) function
    @param[in] FieldOfView FOV angle
    @param[in] AspectRatio Aspect ration
    @param[in] zNear Near plane distance
    @param[in] zFar Far plane distance
*/
void glPerspective (double FieldOfView, double AspectRatio, double zNear, double zFar);

/**
    @brief Subfunction for the glSetPerspective (2) function
    @param[in] Frustum Pointer to the frustum
*/
void glPerspective (FRUSTUM *Frustum);

/**
    @brief Restores OpenGL perspective
*/
void glRestorePerspective ();

/**
    @brief Sets to OpenGL 2D projection mode
    @param[in] View Reference to the view coordinates
    @param[in] Near Near plane distance
    @param[in] Far Far plane distance
*/
void glSetPlanar (const RECTI &View, float Near, float Far);

/**
    @brief Sets to OpenGL 2D projection mode
    @param[in] Near Near plane distance
    @param[in] Far Far plane distance
*/
void glSetPlanar (float Near = -1.0f, float Far = 1.0f);

/**
    @brief Sets to OpenGL 2D projection mode
    @param[in] View Reference to the view coordinates
    @param[in] Frustum Pointer to the view frustum
*/
void glSetPlanar (const RECTI &View, FRUSTUM *Frustum);

/**
    @brief Projects a 3D ray from a pixel on the screen
    @param[in] Position Reference to the pixel position
    @return The 3D ray
*/
RAY3D ProjectPerspective (const VEC2I &Position);

}

#endif
